<script setup>
import { ref, reactive, computed, watch, onMounted } from "vue";
import { ElForm, ElFormItem, ElRow, ElCol } from "element-plus";
import { componentMap } from "./helper/componentMap";
import { getSlot } from "@/utils/helper";
import {
  setTextPlaceholder,
  setGridProp,
  setComponentProps,
  setItemComponentSlots,
  initModel,
} from "./helper";
import { findIndex } from "@/utils";
import { get, set } from "lodash";
import useRenderSelect from "./components/useRenderSelect";
import useRenderRadio from "./components/useRenderRadio";
import useRenderCheckbox from "./components/useRenderCheckbox";
import { useDesign } from "@/hooks/web/useDesign";

const props = defineProps({
  schema: { type: Array, default: () => [] },
  isCol: { type: Boolean, default: true },
  model: { type: Object, default: () => ({}) },
  autoSetPlaceholder: { type: Boolean, default: true },
  isCustom: { type: Boolean, default: false },
  labelWidth: { type: [String, Number], default: "auto" },
  rules: { type: Object, default: () => ({}) },
  labelPosition: { type: String, default: "right" },
  labelSuffix: { type: String, default: "" },
  hideRequiredAsterisk: { type: Boolean, default: false },
  requireAsteriskPosition: { type: String, default: "left" },
  showMessage: { type: Boolean, default: true },
  inlineMessage: { type: Boolean, default: false },
  statusIcon: { type: Boolean, default: false },
  validateOnRuleChange: { type: Boolean, default: true },
  size: { type: String, default: undefined },
  disabled: { type: Boolean, default: false },
  scrollToError: { type: Boolean, default: false },
  scrollToErrorOffset: { type: [Boolean, Object], default: undefined },
});

const emit = defineEmits(["register"]);

const { getPrefixCls } = useDesign();
const prefixCls = getPrefixCls("form");

const elFormRef = ref();
const formModel = ref({ ...props.model });
const mergeProps = ref({});

const formComponents = ref({});
const formItemComponents = ref({});

const getProps = computed(() => ({ ...props, ...mergeProps.value }));

const setValues = (data = {}) => {
  formModel.value = Object.assign({}, formModel.value, data);
};

const setProps = (newProps = {}) => {
  mergeProps.value = Object.assign({}, mergeProps.value, newProps);
};

const delSchema = (field) => {
  const index = findIndex(getProps.value.schema, (v) => v.field === field);
  if (index > -1) getProps.value.schema.splice(index, 1);
};

const addSchema = (formSchema, index) => {
  if (index !== void 0) getProps.value.schema.splice(index, 0, formSchema);
  else getProps.value.schema.push(formSchema);
};

const setSchema = (schemaProps) => {
  for (const v of getProps.value.schema) {
    for (const item of schemaProps) {
      if (v.field === item.field) {
        set(v, item.path, item.value);
      }
    }
  }
};

const getComponentExpose = (field) => formComponents.value[field];
const getFormItemExpose = (field) => formItemComponents.value[field];

onMounted(() => {
  emit("register", elFormRef.value?.$parent, elFormRef.value);
});

watch(
  () => getProps.value.schema,
  (schema = []) => {
    formModel.value = initModel(schema, formModel.value);
  },
  { immediate: true, deep: true }
);

defineExpose({
  setValues,
  formModel,
  setProps,
  delSchema,
  addSchema,
  setSchema,
  getComponentExpose,
  getFormItemExpose,
});
</script>

<template>
  <el-form
    ref="elFormRef"
    v-bind="getProps"
    :model="getProps.isCustom ? getProps.model : formModel"
    :class="prefixCls"
    @submit.prevent
  >
    <template v-if="!getProps.isCustom">
      <el-row v-if="getProps.isCol" :gutter="20">
        <template v-for="item in getProps.schema" :key="item.field">
          <el-col v-bind="item.colProps" v-if="!item.remove">
            <el-form-item
              :label="item.label"
              :prop="item.field"
              v-bind="item.formItemProps"
              v-show="!item.hidden"
            >
              <component
                :is="componentMap[item.component]"
                v-model="formModel[item.field]"
                v-bind="item.componentProps"
                :style="item.componentProps?.style || { width: '100%' }"
              />
            </el-form-item>
          </el-col>
        </template>
      </el-row>
    </template>
    <slot v-else />
  </el-form>
</template>

<style scoped lang="less">
.@{elNamespace}-form.@{adminNamespace}-form .@{elNamespace}-row {
  margin-right: 0 !important;
  margin-left: 0 !important;
}
</style>